/**
 * www.easyplatform.cn ©2016
 */
package cn.easyplatform.studio.web.editors.page;

import cn.easyplatform.entities.BaseEntity;
import cn.easyplatform.entities.beans.list.ListBean;
import cn.easyplatform.entities.beans.page.PageBean;
import cn.easyplatform.entities.beans.table.TableField;
import cn.easyplatform.lang.Mirror;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.studio.StudioApp;
import cn.easyplatform.studio.cmd.entity.GetAndLockEntityCmd;
import cn.easyplatform.studio.context.Contexts;
import cn.easyplatform.studio.utils.WebUtils;
import cn.easyplatform.studio.vos.AccessVo;
import cn.easyplatform.studio.vos.DeviceVo;
import cn.easyplatform.studio.vos.GetEntityVo;
import cn.easyplatform.studio.web.editors.*;
import cn.easyplatform.studio.web.editors.entity.ListEntityEditor;
import cn.easyplatform.studio.web.editors.support.ChildDelegate;
import cn.easyplatform.studio.web.editors.support.CodeFormatter;
import cn.easyplatform.studio.web.editors.support.ZulXsdUtil;
import cn.easyplatform.studio.web.layout.WorkbenchController;
import cn.easyplatform.studio.web.views.impl.AbstractView;
import cn.easyplatform.type.EntityType;
import cn.easyplatform.type.FieldType;
import cn.easyplatform.web.ext.EntityExt;
import cn.easyplatform.web.ext.ZkExt;
import cn.easyplatform.web.ext.cmez.CMeditor;
import cn.easyplatform.web.ext.echarts.ECharts;
import cn.easyplatform.web.ext.echarts.builder.EChartsBuilderFactory;
import cn.easyplatform.web.ext.echarts.builder.IEChartsBuilder;
import nu.xom.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.zkoss.util.resource.Labels;
import org.zkoss.util.resource.Locators;
import org.zkoss.zk.ui.*;
import org.zkoss.zk.ui.event.DropEvent;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.sys.DesktopCtrl;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.*;
import org.zkoss.zul.impl.InputElement;
import org.zkoss.zul.impl.XulElement;

import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author <a href="mailto:shiny_vc@163.com">陈云亮</a> <br/>
 * @since 2.0.0 <br/>
 */
public class PageDesigner implements EventListener<Event> {
    public void setDragListener(OnDragListener dragListener) {
        this.dragListener = dragListener;
    }

    public interface OnDragListener {
        void getDevice(DeviceVo deviceVo);
    }

    private Document document;

    private Component canvas;

    private CMeditor editor;

    private WorkbenchController workbench;

    private PageEditor page;

    private String zulMapXml;

    private Element selection;

    private boolean isMobile;

    private List<AccessVo> accessVos;

    private OnDragListener dragListener;
    /**
     * @param workbench
     */
    public PageDesigner(WorkbenchController workbench, PageEditor page,
                        Component canvas, CMeditor editor, boolean isMobile, List<AccessVo> accessVos) {
        this.workbench = workbench;
        this.page = page;
        this.canvas = canvas;
        this.editor = editor;
        this.isMobile = isMobile;
        this.accessVos = accessVos;
        this.editor.addEventListener(CMeditor.ON_CURSOR_ACTIVITY, this);
        this.editor.setDroppable(EntityType.TABLE.getName());
        ((HtmlBasedComponent) this.canvas).setDroppable(EntityType.TABLE
                .getName());
        this.editor.addEventListener(Events.ON_DROP, this);
        this.canvas.addEventListener(Events.ON_DROP, this);
        Tab tab = new Tab();
        tab.setAutoClose(true);
    }

    public void redraw(Boolean isChangeCode) {
        Component children = canvas.getFirstChild();
        if (children != null)
            children.detach();
        String content = (String) this.editor.getValue();
        if (Strings.isBlank(content)) {
            if (document != null)
                document.detach();
            document = null;
            page.showMsg("", editor);
        } else {
            if (content.contains("<![CDATA[")) {
                page.showMsg(Labels.getLabel("editor.error.CDATA"), editor);
            } else {
                try {
                    String content1 = content;
                    if (isMobile)
                        content1 = "<div width=\"92%\" height=\"76%\" style=\"position: relative;top: 12%;left: 4%;\">" + content + "</div>";
                    Component c = Executions.createComponentsDirectly(content1,
                            "zul", canvas, null);
                    document = mapZulToComponents(content, c);
                    zulMapXml = StringUtils.removeStart(document.toXML(),
                            "<?xml version=\"1.0\"?>\n");
                    Iterator<Component> itr = c.queryAll("*").iterator();
                    List<Component> cs = new ArrayList<Component>();
                    while (itr.hasNext()) {
                        Component comp = itr.next();
                        if (comp instanceof HtmlBasedComponent) {
                            HtmlBasedComponent widget = (HtmlBasedComponent) comp;
                            widget.setDroppable(EntityType.TABLE.getName());
                            widget.addEventListener(Events.ON_DROP, this);
                            if (widget instanceof LayoutRegion) {// zk不响应单击事件
                                widget.setWidgetOverride("doClick_",
                                        "function(e){epstudio.selectWidget('"
                                                + canvas.getUuid()
                                                + "',this);this.fireX(e)}");
                            } else {
                                widget.setWidgetListener(
                                        Events.ON_CLICK,
                                        "epstudio.selectWidget('"
                                                + canvas.getUuid() + "',this)");
                            }
                            if (widget instanceof ECharts) {
                                widget.addEventListener(Events.ON_DOUBLE_CLICK, this);
                            } else {
                                widget.addEventListener(Events.ON_CLICK, this);
                            }
                            if (!ZulXsdUtil.isContainer(comp.getDefinition()
                                    .getName()))
                                widget.setDraggable(EntityType.TABLE.getName());
                            if (comp instanceof ECharts) {
                                ECharts eCharts = (ECharts) comp;
                                String type = null;
                                if (eCharts.getType() != null)
                                    type = eCharts.getType();

                                if (Strings.isBlank(type) == false) {
                                    IEChartsBuilder builder = EChartsBuilderFactory.createBuilder(type);
                                    builder.build(eCharts, null);
                                    /*if (isChangeCode == Boolean.FALSE)
                                        drawEcharts(eCharts, content, -1, -1);*/
                                }
                            }
                        }
                        if (comp instanceof InputElement)
                            ((InputElement) comp).setConstraint((Constraint) null);
                        else if (comp instanceof ZkExt || comp instanceof EntityExt)
                            cs.add(comp);
                    }
                    for (Component comp : cs)
                        ComponentBuilder.create(comp);
                    /*if (document instanceof Document) {
                        if ("echarts".equals(document.getRootElement().getLocalName())) {
                            // 如果根元素是zk标签
                            String type = null;
                            if (document.getRootElement().getAttribute("type") != null)
                                type = document.getRootElement().getAttribute("type").getValue();

                            if (Strings.isBlank(type) == false) {
                                EChartsBuilder builder = EChartsBuilderFactory.createBuilder(type);
                                builder.build((ECharts) c, null);
                                if (isChangeCode == Boolean.FALSE)
                                    drawEcharts((ECharts) c, content, -1, -1);
                            }
                        }
                    }*/
                    page.showMsg("", editor);
                    Clients.evalJavaScript("jq('.z-error').remove()");
                } catch (Exception ex) {
                    ex.printStackTrace();
                    if (canvas.getFirstChild() != null)
                        canvas.getFirstChild().detach();
                    if (children != null)
                        children.setParent(canvas);
                    String msg = ex.getMessage();
                    if (msg != null) {
                        int idx = msg.indexOf("for class");
                        if (idx > 0)
                            msg = msg.substring(0, idx);
                        else if ((idx = msg.indexOf("SAXParseException;")) >= 0) {
                            msg = msg.substring(idx + 18);
                        }
                    } else
                        msg = "Wrong page content";
                    page.showMsg(msg, editor);
                }
            }
        }

        //将projectID传入servlet，获取apps路径进行渲染
        editor.getDesktop().getSession().setAttribute("projectName", Contexts.getProject().getId());
    }

    private Document mapZulToComponents(String content, final Component root) {
        Document document = ZulXsdUtil.buildDocument(content);
        if (document.getRootElement() == null)
            return document;

        Map<String, Object> params = new HashMap<String, Object>();
        ZulXsdUtil.traverseChildren(document.getRootElement(), params,
                new ChildDelegate<Element>() {
                    @Override
                    public void onChild(Element child,
                                        Map<String, Object> params) {
                        String s = child.getDocument().toXML();
                        //System.out.println(s);
                        if (child.getParent() instanceof Document) {
                            if ("web".equals(child.getLocalName())) {
                                // 如果根元素是zk标签
                                child.addAttribute(new Attribute("uuid",
                                        "_canvas_"));
                            }
                        }
                        if ("web".equals(child.getLocalName()))
                            return;
                        else if ("zscript".equals(child.getLocalName())) {
                            // zsscript不是可显示的组件，但是通过给uuid就可以象正常的组件一样处理
                            child.addAttribute(new Attribute("uuid", "zscript_"
                                    + getNextUuid()));
                            return;
                        } else if ((ZulXsdUtil.isNative(child) && ZulXsdUtil
                                .isNative((Element) child.getParent()))
                                && (!hasNonNativeSiblings(child) || child
                                .getChildElements().size() == 0)) {
                            child.addAttribute(new Attribute("uuid", "native_"
                                    + getNextUuid()));
                            return;
                        }

                        Element parent = getParent(child);
                        if (parent == null) {
                            if (doMatch(child, root)) {
                                child.addAttribute(new Attribute("uuid", root
                                        .getUuid()));
                            }
                        } else {
                            String uuid = parent.getAttributeValue("uuid");
                            if (uuid != null) {
                                if (uuid.startsWith("native_")) {
                                    parent = (Element) parent.getParent();
                                    if (parent.getAttribute("uuid") != null) {
                                        while (parent.getAttributeValue("uuid")
                                                .startsWith("native_")) {
                                            parent = (Element) parent.getParent();
                                            if (parent.getAttribute("uuid") == null)
                                                break;
                                        }
                                        if (parent.getAttribute("uuid") != null)
                                            uuid = parent.getAttributeValue("uuid");
                                    }
                                }
                                if (uuid != null) {
                                    Component c = root.query(
                                            "[uuid^=\"" + uuid + "\"]");
                                    if (c != null) {
                                        Collection<Component> children = c.getChildren();
                                        for (Component comp : children) {
                                            if (doMatch(child, comp)) {
                                                child.addAttribute(new Attribute(
                                                        "uuid", comp.getUuid()));
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                });
        return document;
    }

    private Element getParent(Element element) {
        Node parent = element.getParent();
        while (parent != null) {
            if (parent instanceof Element
                    && !"web".equals(((Element) parent).getLocalName())
                    && !"template".equals(((Element) parent).getLocalName())) {
                break;
            }
            parent = parent.getParent();
        }

        return parent instanceof Element ? (Element) parent : null;
    }

    private boolean doMatch(Element element, Component component) {
        boolean match;
        match = isAvailable(element.getDocument().getRootElement(),
                component.getUuid());
        if (!match)
            return false;
        if (component instanceof HtmlNativeComponent) {
            return element.getLocalName().equals(
                    ((HtmlNativeComponent) component).getTag())
                    && ZulXsdUtil.isNative(element);
        } else {
            return element.getLocalName().equals(
                    component.getDefinition().getName());
        }
    }

    private boolean isAvailable(Element root, String uuid) {
        // XPathContext xpathContext = new XPathContext("zul", ZUL_NS);
        Nodes nodes = root.query("//*[@uuid='" + uuid + "']", null);
        return nodes.size() == 0;
    }

    private boolean hasNonNativeSiblings(Element element) {
        Node parent = element.getParent();
        if (!(parent instanceof Element))
            return false;

        for (int i = 0; i < ((Element) parent).getChildElements().size(); i++) {
            Element child = ((Element) parent).getChildElements().get(i);
            if (!child.equals(element) && !ZulXsdUtil.isNative(child)) {
                return true;
            }
        }
        return false;
    }

    private Element getElementAttribute(Element element, String name) {
        Elements attributes = selection.getChildElements("attribute");
        for (int i = 0; i < attributes.size(); i++) {
            Element elm = attributes.get(i);
            if (elm.getAttributeValue("name").equals(name))
                return elm;
        }
        return null;
    }

    private String getNextUuid() {
        Desktop desktop = canvas.getDesktop();
        return ((DesktopCtrl) desktop).getNextUuid(desktop.getFirstPage());
    }

    @SuppressWarnings("unchecked")
    @Override
    public void onEvent(Event event) throws Exception {
        if (event.getTarget() == editor) {
            if (event.getName().equals(CMeditor.ON_CURSOR_ACTIVITY)) {
                onZulCodeClick((Map<String, Object>) event.getData());
            } else {
                onDrop((DropEvent) event);
            }
        } else if (event.getName().equals(Events.ON_DROP)) {
            onDrop((DropEvent) event);
        } else if (event.getName().equals(Events.ON_CLICK) || event.getTarget() instanceof ECharts) {
            onSelectWidget(event.getTarget(), false);
        }
    }

    private void onSelectWidget(final Component sel, boolean fromEditor) {
        selection = getElementByUuid(sel.getUuid());
        if (selection == null)
            return;
        if (!fromEditor) {
            String[] lines = zulMapXml.split("\n");
            int lineNo = -1, charNo = -1;
            Pattern pattern;
            String regex, tag = selection.getQualifiedName();
            if (!"attribute".equals(tag)) {
                regex = "(^\\s*)(<" + tag + "\\s{1,1})(.*)(\\s*uuid=\""
                        + sel.getUuid() + "\")";
            } else {
                String preffix = "";
                Attribute attribute = selection.getAttribute("name");
                if (attribute == null) {
                    String clientNS = ZulXsdUtil
                            .getClientNamespace((cn.easyplatform.studio.web.editors.support.Element) selection);
                    attribute = selection.getAttribute("name", clientNS);
                    preffix = attribute.getNamespacePrefix() + ":";
                }
                regex = "(^\\s*)(<" + tag + "\\s{1,1})(.*)(\\s*" + preffix
                        + "name=\"" + attribute.getValue() + "\")";
            }
            pattern = Pattern.compile(regex);
            Matcher matcher;
            for (String line : lines) {
                lineNo++;
                matcher = pattern.matcher(line);
                if (matcher.find()) {
                    for (int m = 0; m <= matcher.groupCount(); m++) {
                        if (matcher.group(m).startsWith("<")) {
                            charNo = matcher.start(m);
                            break;
                        }
                    }
                    break;
                }
            }
            if (lineNo >= 0 && charNo >= 0) {
                editor.setCursor(lineNo, charNo);
                /*----------------------------点击echart标签部分逻辑------------------------------*/
                if ("echarts".equals(tag)) {
                    final int line = lineNo;
                    String[] lineArr = editor.getValue().toString().split("\n");
                    int endline = -1;
                    for (int i = line; i < lineArr.length; i++) {
                        String val = lineArr[i];
                        if (val.contains("</echarts>") || val.contains("/>")) {
                            endline = i;
                            break;
                        }
                    }
                    if (endline != -1) {
                        StringBuilder stringBuilder = new StringBuilder();
                        for (int i = line; i <= endline; i++) {
                            stringBuilder.append(lineArr[i] + "\n");
                        }
                        final String echartXml = stringBuilder.toString();
                        final int finalEndline = endline;
                        drawEcharts((ECharts) sel, echartXml, finalEndline, line);
                    }
                }
                /*----------------------------点击echart标签部分逻辑------------------------------*/
            }
        }
        PropertityChangeListener el = new PropertityChangeListener(sel);
        if ("attribute".equals(selection.getLocalName())) {

        } else if ("zscript".equals(selection.getLocalName())) {

        } else if (!ZulXsdUtil.isBaseGroupElement(selection)
                && !ZulXsdUtil.isNative(selection)) {
            SortedMap<String, SortedSet<Element>> attributes = ZulXsdUtil
                    .getWidgetDescription(sel.getDefinition().getName());
            List<Map<String, Component>> properties = new ArrayList<Map<String, Component>>(
                    2);
            properties.add(new TreeMap<String, Component>());
            properties.add(new TreeMap<String, Component>());
            Map<String, Component> events = new TreeMap<String, Component>();
            for (Map.Entry<String, SortedSet<Element>> entry : attributes
                    .entrySet()) {
                Map<String, Component> map = null;
                if (entry.getKey().equals("abstractComponentAttrGroup")
                        || entry.getKey().equals("xulElementAttrGroup")
                        || entry.getKey().equals("htmlBasedComponentAttrGroup"))
                    map = properties.get(0);
                else
                    map = properties.get(1);
                for (Element property : entry.getValue()) {
                    String propertyName = property.getAttributeValue("name");
                    String type = property.getAttributeValue("type");
                    if (isEvent(propertyName) || type.equals("pageEventType")
                            || type.equals("logicEventType")) {
                        String mode = "epscript";
                        if (propertyName.startsWith("on"))
                            mode = "text/x-java";
                        else if (type.equals("pageEventType"))
                            mode = "javascript";
                        Button btn = createEventButton(propertyName,
                                "z-icon-bolt");
                        btn.addEventListener(Events.ON_CLICK,
                                new PageEventListener(mode, type));
                        events.put(propertyName, btn);
                    } else {
                        Component editor = createEditor(sel, el, propertyName,
                                property);

                        if (type.equals("logicEntityType"))
                            events.put(propertyName, editor);
                        else
                            map.put(propertyName, editor);
                    }
                }
            }
            page.updateProperties(selection, properties, events);
        }
    }

    private void onDrop(DropEvent evt) {
        Component target = null;
        if (evt instanceof cn.easyplatform.web.ext.cmez.DropEvent) {
            cn.easyplatform.web.ext.cmez.DropEvent de = (cn.easyplatform.web.ext.cmez.DropEvent) evt;
            if (document == null)
                target = canvas;
            else
                target = getComponentByCursor(de.getLine(), de.getCh());
        } else
            target = evt.getTarget();
        if (target != null) {
            Component source = evt.getDragged();
            if (target == canvas) {
                target = canvas.getFirstChild();
                if (target == null) {//空内容
                    if (source instanceof Toolbarbutton) {
                        String name = ((Toolbarbutton) source).getLabel().toLowerCase();
                        if (ZulXsdUtil.isContainer(name)) {
                            try {
                                editor.setValue(IOUtils.toString(Locators.getDefault()
                                        .getResourceAsStream("web/template/" + name + ".zul")));
                                redraw(false);
                            } catch (Exception ex) {
                                page.showMsg(ex.getMessage(), editor);
                            }
                        } else {
                            editor.setValue("<div hflex=\"1\" vflex=\"1\"></div>");
                            redraw(false);
                        }
                    } else {
                        editor.setValue("<div></div>");
                        redraw(false);
                    }
                    target = canvas.getFirstChild();
                }
            }
            getCWithC(target, null);
            if (isMobile && target != null &&target.getChildren().size() > 0) {
                target.removeChild(target.getChildren().get(0));
            }
            if (source instanceof Row) {
                if (document == null) {
                    WebUtils.showError(Labels.getLabel("entity.page.no.parent"));
                    return;
                }
                Row row = (Row) source;
                TableField tf = row.getValue();
                if (page instanceof ListEntityEditor && (tf.getType() == FieldType.DATETIME
                        || tf.getType() == FieldType.DATE || tf.getType() == FieldType.TIME
                        || tf.getType() == FieldType.INT || tf.getType() == FieldType.LONG
                        || tf.getType() == FieldType.NUMERIC)) {
                    Label label1 = new Label(tf.getDescription());
                    Label label2 = new Label("-");
                    XulElement input1 = WebUtils.createComponent(tf);
                    XulElement input2 = WebUtils.createComponent(tf);
                    input1.setId("#{" + tf.getName() + "}1");
                    input2.setId("#{" + tf.getName() + "}2");
                    Combobox combobox = new Combobox();
                    if (ZulXsdUtil.isAblechild(target.getDefinition().getName(),
                            input1.getDefinition().getName())) {
                        drawTableFieldForDate(target, label1, input1, label2, input2, combobox, tf, -1);
                    } else {
                        Component parent = target.getParent();
                        if (parent != null && parent != canvas && ZulXsdUtil.isAblechild(
                                parent.getDefinition().getName(), input1.getDefinition().getName())) {
                            drawTableFieldForDate(parent, label1, input1, label2, input2, combobox, tf, parent
                                    .getChildren().indexOf(target));
                        }
                    }
                } else {
                    Label label = new Label(tf.getDescription());
                    XulElement input = WebUtils.createComponent(tf);
                    input.setId("#{" + tf.getName() + "}");
                    if (ZulXsdUtil.isAblechild(target.getDefinition().getName(),
                            input.getDefinition().getName())) {
                        drawTableField(target, label, input, -1);
                    } else {
                        Component parent = target.getParent();
                        if (parent != null
                                && parent != canvas
                                && ZulXsdUtil
                                .isAblechild(parent.getDefinition()
                                        .getName(), input.getDefinition()
                                        .getName())) {
                            drawTableField(parent, label, input, parent
                                    .getChildren().indexOf(target));
                        }
                    }
                }
            } else if (source instanceof Toolbarbutton
                    && source.getAttribute("t") != null) {
                String name = ((Toolbarbutton) source).getLabel().toLowerCase();
                if (ZulXsdUtil.isAblechild(target.getDefinition().getName(), name)) {
                    drawTemplate(target, name, -1, null);
                } else {
                    Component parent = target.getParent();
                    if (parent != null
                            && parent != canvas
                            && ZulXsdUtil.isAblechild(parent.getDefinition()
                            .getName(), name)) {
                        drawTemplate(parent, name, parent.getChildren()
                                .indexOf(target), null);
                    } else {
                        page.updateContent(editor.getValue().toString());
                    }
                }
            } else if (source instanceof Vlayout
                    && source.getAttribute("t") != null) {
                String name = ((Vlayout) source).getId();
                String content = (String) source.getAttribute("t");
                DeviceVo deviceVo = (DeviceVo) source.getAttribute("s");
                drawTemplate(target, name, -1, content);
                if (dragListener != null) {
                    dragListener.getDevice(deviceVo);
                }
                /*if (ZulXsdUtil.isAblechild(target.getDefinition().getName(), name)) {
                    drawTemplate(target, name, -1);
                } else {
                    Component parent = target.getParent();
                    if (parent != null
                            && parent != canvas
                            && ZulXsdUtil.isAblechild(parent.getDefinition()
                            .getName(), name)) {
                        drawTemplate(parent, name, parent.getChildren()
                                .indexOf(target));
                    } else {
                        page.updateContent(editor.getValue().toString());
                    }
                }*/
            } else if (source instanceof Image) {
                String name = ((Image) source).getAttribute("name") + "";
                if (name.contains("bar") || name.contains("funnel") || name.contains("line") || name.contains("pie") || name.contains("gauge")) {
                    if (ZulXsdUtil.isAblechild(target.getDefinition().getName(), "echarts")) {
                        drawTemplate(target, name, -1, null);
                    }
                } else if (ZulXsdUtil.isAblechild(target.getDefinition().getName(),
                        name)) {
                    drawTemplate(target, name, -1, null);
                } else {
                    Component parent = target.getParent();
                    if (parent != null
                            && parent != canvas
                            && ZulXsdUtil.isAblechild(parent.getDefinition()
                            .getName(), name)) {
                        drawTemplate(parent, name, parent.getChildren()
                                .indexOf(target), null);
                    }
                }
            } else {
                if (ZulXsdUtil.isAblechild(target.getDefinition().getName(),
                        source.getDefinition().getName())) {
                    drawWidget(target, source, -1);
                } else {
                    Component parent = target.getParent();
                    if (parent != null
                            && parent != canvas
                            && ZulXsdUtil.isAblechild(parent.getDefinition()
                            .getName(), source.getDefinition()
                            .getName())) {
                        drawWidget(parent, source, parent.getChildren()
                                .indexOf(target));
                    }
                }
            }
        }
    }

    private void drawEcharts(final ECharts sel, final String echartXml, final int finalEndline, final int line) {
        EChartsBuilderFactory.showConfig(sel, new EventListener<Event>() {
            @Override
            public void onEvent(Event event) throws Exception {
                String content = sel.getContent();
                sel.setContent(content);
                Document document = ZulXsdUtil.buildDocument(echartXml);
                String[] lines = editor.getValue().toString().split("\n");
                document.getRootElement().addAttribute(new Attribute("content", content));
                document.getRootElement().addAttribute(new Attribute("type", sel.getType()));
                document.getRootElement().addAttribute(new Attribute("template", sel.getTemplate()));
                if (finalEndline == -1) {
                    editor.setValue(document.getRootElement().toXML());
                } else {
                    StringBuffer stringBuffer = new StringBuffer();
                    for (int i = 0; i < line; i++) {
                        stringBuffer.append(lines[i] + "\n");
                    }
                    stringBuffer.append(document.getRootElement().toXML());
                    for (int i = finalEndline + 1; i < lines.length; i++) {
                        stringBuffer.append(lines[i] + "\n");
                    }
                    String editorXml = stringBuffer.toString();
                    Document copyDocument = ZulXsdUtil.buildDocument(editorXml);
                    editor.setValue(CodeFormatter.formatXML(copyDocument));
                    //redraw();
                    page.updateContent(CodeFormatter.formatXML(copyDocument));
                }
            }
        });
    }

    private void drawWidget(Component target, Component source, int pos) {
        Element t = getElementByUuid(target.getUuid());
        Element s = getElementByUuid(source.getUuid());
        Component parent = source.getParent();
        int idx = parent.getChildren().indexOf(source);
        source.detach();
        try {
            if (pos < 0) {
                target.appendChild(source);
                s.detach();
                s.getAttribute("uuid").setValue(source.getUuid());
                t.appendChild(s);
            } else {
                target.getChildren().add(pos, source);
                s.detach();
                s.getAttribute("uuid").setValue(source.getUuid());
                t.insertChild(s, pos);
            }
            zulMapXml = CodeFormatter.formatXML(document);
            Document copy = (Document) document.copy();
            ZulXsdUtil.cleanUUIDs(copy.getRootElement());
            page.updateContent(CodeFormatter.formatXML(copy));
            onSelectWidget(source, false);
            Clients.evalJavaScript("epstudio.selectWidget('"
                    + canvas.getUuid() + "','" + source.getUuid() + "')");
        } catch (UiException ex) {
            parent.getChildren().add(idx, source);
            s.getAttribute("uuid").setValue(source.getUuid());
            throw ex;
        }
    }

    private void drawTemplate(Component target, String name, int pos, String content) {
        Component comp = null;
        try {
            String template = null;
            if (Strings.isBlank(content)) {
                template = IOUtils.toString(Locators.getDefault()
                        .getResourceAsStream("web/template/" + name + ".zul"), "utf-8");
            } else {
                template = content;
            }
            if (template == null)
                return;
            comp = Executions.createComponentsDirectly(template, "zul", null,
                    null);
            List<Component> newc = new ArrayList<Component>();
            Iterator<Component> itr = comp.queryAll("*").iterator();
            while (itr.hasNext())
                newc.add(itr.next());
            if (pos >= 0) {
                target.getChildren().add(pos, comp);
            } else
                target.appendChild(comp);
            if (document == null) {
                document = mapZulToComponents(template, comp);
            } else {
                Element parent = getElementByUuid(target.getUuid());
                Document doc = mapZulToComponents(template, comp);
                if (pos < 0)
                    parent.appendChild(doc.getRootElement().copy());
                else
                    insertElement(parent, doc.getRootElement().copy(), pos);
                doc.detach();
                doc = null;
            }
            for (Component c : newc) {
                if (c instanceof HtmlBasedComponent) {
                    HtmlBasedComponent widget = (HtmlBasedComponent) c;
                    widget.setDroppable(EntityType.TABLE.getName());
                    widget.addEventListener(Events.ON_DROP, this);
                    if (widget instanceof LayoutRegion) {// zk不响应单击事件
                        widget.setWidgetOverride("doClick_",
                                "function(e){epstudio.selectWidget('"
                                        + canvas.getUuid()
                                        + "',this);this.fireX(e)}");
                    } else {
                        widget.setWidgetListener(
                                Events.ON_CLICK,
                                "epstudio.selectWidget('"
                                        + canvas.getUuid() + "',this)");
                    }
                    widget.addEventListener(Events.ON_CLICK, this);
                    if (!ZulXsdUtil.isContainer(c.getDefinition().getName()))
                        widget.setDraggable(EntityType.TABLE.getName());
                    if (c instanceof ZkExt || comp instanceof EntityExt)
                        ComponentBuilder.create(c);
                }
            }
            newc = null;
            zulMapXml = CodeFormatter.formatXML(document);
            Document copy = (Document) document.copy();
            ZulXsdUtil.cleanUUIDs(copy.getRootElement());
            page.updateContent(CodeFormatter.formatXML(copy));
            onSelectWidget(comp, false);
            Clients.evalJavaScript("epstudio.selectWidget('"
                    + canvas.getUuid() + "','" + comp.getUuid() + "')");
        } catch (Exception ex) {
            if (comp != null)
                comp.detach();
            if (ex instanceof UiException)
                throw (UiException) ex;
        }
    }

    private void getCWithC(Component component, List<String> oldList) {
        if (oldList == null)
            oldList = new ArrayList<>();
        if (component.getChildren() != null) {
            for (Component c : component.getChildren()) {
                oldList.add(c.getUuid());
                getCWithC(c, oldList);
            }
        }
        System.out.println(oldList);
    }

    private void insertElement(Element parent, Node elm, int pos) {
        int size = parent.getChildCount();
        int count = 0, idx = 0;
        for (; idx < size; idx++) {
            Node node = parent.getChild(idx);
            if (node instanceof Element)
                count++;
            if (count == pos)
                break;
        }
        parent.insertChild(elm, idx + 1);
    }

    private void drawTableField(Component target, Label label,
                                XulElement input, int pos) {
        try {
            if (pos < 0)
                target.appendChild(label);
            else
                target.getChildren().add(pos, label);
            if (pos < 0)
                target.appendChild(input);
            else
                target.getChildren().add(pos + 1, input);
        } catch (UiException ex) {
            label.detach();
            input.detach();
            throw ex;
        }
        label.setDraggable(EntityType.TABLE.getName());
        label.setDroppable(label.getDraggable());
        label.addEventListener(Events.ON_DROP, this);
        label.setWidgetListener(Events.ON_CLICK,
                "epstudio.selectWidget('" + canvas.getUuid() + "',this)");
        label.addEventListener(Events.ON_CLICK, this);
        input.setDraggable(EntityType.TABLE.getName());
        input.setDroppable(label.getDraggable());
        input.addEventListener(Events.ON_DROP, this);
        input.setWidgetListener(Events.ON_CLICK,
                "epstudio.selectWidget('" + canvas.getUuid() + "',this)");
        input.addEventListener(Events.ON_CLICK, this);

        Element parent = getElementByUuid(target.getUuid());
        if (parent == null)
            parent = document.getRootElement();
        Element elm = new Element(label.getDefinition().getName());
        elm.addAttribute(new Attribute("value", label.getValue()));
        elm.addAttribute(new Attribute("uuid", label.getUuid()));
        if (pos < 0)
            parent.appendChild(elm);
        else
            insertElement(parent, elm, pos);

        elm = new Element(input.getDefinition().getName());
        elm.addAttribute(new Attribute("id", input.getId()));
        elm.addAttribute(new Attribute("uuid", input.getUuid()));
        if (pos < 0)
            parent.appendChild(elm);
        else
            insertElement(parent, elm, pos + 1);

        zulMapXml = CodeFormatter.formatXML(document);
        Document copy = (Document) document.copy();
        ZulXsdUtil.cleanUUIDs(copy.getRootElement());
        page.updateContent(CodeFormatter.formatXML(copy));
        onSelectWidget(input, false);
        Clients.evalJavaScript("epstudio.selectWidget('"
                + canvas.getUuid() + "','" + input.getUuid() + "')");
    }

    private void drawTableFieldForDate(Component target, Label label1,
                                       XulElement input1, Label label2, XulElement input2, Combobox combobox, TableField tf, int pos) {
        Hbox hbox = new Hbox();
        try {
            if (pos < 0)
                target.appendChild(label1);
            else
                target.getChildren().add(pos, label1);
            if (pos < 0)
                target.appendChild(hbox);
            else
                target.getChildren().add(pos + 1, hbox);
            if (pos < 0)
                hbox.appendChild(combobox);
            else
                hbox.getChildren().add(pos + 1, combobox);
            if (pos < 0)
                hbox.appendChild(input1);
            else
                hbox.getChildren().add(pos + 2, input1);
            if (pos < 0)
                hbox.appendChild(label2);
            else
                hbox.getChildren().add(pos + 3, label2);
            if (pos < 0)
                hbox.appendChild(input2);
            else
                hbox.getChildren().add(pos + 4, input2);
        } catch (UiException ex) {
            label1.detach();
            input1.detach();
            label2.detach();
            input2.detach();
            combobox.detach();
            throw ex;
        }
        label1.setDraggable(EntityType.TABLE.getName());
        label1.setDroppable(label1.getDraggable());
        label1.addEventListener(Events.ON_DROP, this);
        label1.setWidgetListener(Events.ON_CLICK, "epstudio.selectWidget('" + canvas.getUuid() + "',this)");
        label1.addEventListener(Events.ON_CLICK, this);

        hbox.setDraggable(EntityType.TABLE.getName());
        hbox.setDroppable(label1.getDraggable());
        hbox.addEventListener(Events.ON_DROP, this);
        hbox.setWidgetListener(Events.ON_CLICK, "epstudio.selectWidget('" + canvas.getUuid() + "',this)");
        hbox.addEventListener(Events.ON_CLICK, this);

        input1.setDraggable(EntityType.TABLE.getName());
        input1.setDroppable(label1.getDraggable());
        input1.addEventListener(Events.ON_DROP, this);
        input1.setWidgetListener(Events.ON_CLICK, "epstudio.selectWidget('" + canvas.getUuid() + "',this)");
        input1.addEventListener(Events.ON_CLICK, this);

        label2.setDraggable(EntityType.TABLE.getName());
        label2.setDroppable(label2.getDraggable());
        label2.addEventListener(Events.ON_DROP, this);
        label2.setWidgetListener(Events.ON_CLICK, "epstudio.selectWidget('" + canvas.getUuid() + "',this)");
        label2.addEventListener(Events.ON_CLICK, this);

        input2.setDraggable(EntityType.TABLE.getName());
        input2.setDroppable(label2.getDraggable());
        input2.addEventListener(Events.ON_DROP, this);
        input2.setWidgetListener(Events.ON_CLICK, "epstudio.selectWidget('" + canvas.getUuid() + "',this)");
        input2.addEventListener(Events.ON_CLICK, this);

        combobox.setDraggable(EntityType.TABLE.getName());
        combobox.setDroppable(input2.getDraggable());
        combobox.addEventListener(Events.ON_DROP, this);
        combobox.setWidgetListener(Events.ON_CLICK, "epstudio.selectWidget('" + canvas.getUuid() + "',this)");
        combobox.addEventListener(Events.ON_CLICK, this);

        Element parent = getElementByUuid(target.getUuid());
        if (parent == null)
            parent = document.getRootElement();
        Element elm = new Element(label1.getDefinition().getName());
        elm.addAttribute(new Attribute("value", label1.getValue()));
        elm.addAttribute(new Attribute("uuid", label1.getUuid()));
        if (pos < 0)
            parent.appendChild(elm);
        else
            insertElement(parent, elm, pos);

        Element hboxElm = new Element(hbox.getDefinition().getName());
        hboxElm.addAttribute(new Attribute("uuid", hbox.getUuid()));
        if (pos < 0)
            parent.appendChild(hboxElm);
        else
            insertElement(parent, hboxElm, pos + 1);

        elm = new Element(combobox.getDefinition().getName());
        elm.addAttribute(new Attribute("for", tf.getName()));
        elm.addAttribute(new Attribute("type", "op"));
        elm.addAttribute(new Attribute("uuid", combobox.getUuid()));
        elm.addAttribute(new Attribute("value", "between,not between"));
        if (pos < 0)
            hboxElm.appendChild(elm);
        else
            insertElement(hboxElm, elm, pos + 1);

        elm = new Element(input1.getDefinition().getName());
        elm.addAttribute(new Attribute("id", input1.getId()));
        elm.addAttribute(new Attribute("uuid", input1.getUuid()));
        if (pos < 0)
            hboxElm.appendChild(elm);
        else
            insertElement(hboxElm, elm, pos + 2);

        elm = new Element(label2.getDefinition().getName());
        elm.addAttribute(new Attribute("value", label2.getValue()));
        elm.addAttribute(new Attribute("uuid", label2.getUuid()));
        if (pos < 0)
            hboxElm.appendChild(elm);
        else
            insertElement(hboxElm, elm, pos + 3);

        elm = new Element(input2.getDefinition().getName());
        elm.addAttribute(new Attribute("id", input2.getId()));
        elm.addAttribute(new Attribute("uuid", input2.getUuid()));
        if (pos < 0)
            hboxElm.appendChild(elm);
        else
            insertElement(hboxElm, elm, pos + 4);

        zulMapXml = CodeFormatter.formatXML(document);
        Document copy = (Document) document.copy();
        ZulXsdUtil.cleanUUIDs(copy.getRootElement());
        page.updateContent(CodeFormatter.formatXML(copy));
        onSelectWidget(input1, false);
        Clients.evalJavaScript("epstudio.selectWidget('"
                + canvas.getUuid() + "','" + input1.getUuid() + "')");
    }

    private Element getElementByUuid(final String uuid) {
        Nodes nodes = document.getRootElement()
                .query("descendant-or-self::*[@uuid='" + uuid + "']");
        if (nodes.size() > 0)
            return (Element) nodes.get(0);
        return null;
    }

    private void onZulCodeClick(Map<String, Object> objs) {
        int line = (Integer) objs.get("line");
        int ch = (Integer) objs.get("ch");
        Component c = getComponentByCursor(line, ch);
        if (c != null) {
            Clients.evalJavaScript("epstudio.selectWidget('"
                    + canvas.getUuid() + "','" + c.getUuid() + "')");
            onSelectWidget(c, true);
        } else
            page.updateProperties(null, null, null);
    }

    private Component getComponentByCursor(int line, int ch) {
        if (Strings.isBlank(zulMapXml))
            return null;
        try {
            String[] lines = zulMapXml.split("\n");
            if (lines.length <= line)
                return null;
            Pattern pattern = Pattern.compile("\\s+uuid\\s*=\\s*\\\"([^\\\"]*?)\\\"", Pattern.CASE_INSENSITIVE);
            Matcher matcher = pattern.matcher(lines[line]);
            String c = null;
            while (matcher.find()) {
                c = matcher.group(1);
                System.out.println("group1=" + matcher.group(1));
            }
            if (c != null)
                return canvas.query("[uuid^=\""
                        + c + "\"]");
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    private boolean isEvent(String name) {
        return name.startsWith("on") && name.length() > 2
                && name.codePointAt(2) >= "A".codePointAt(0);
    }

    private Button createEventButton(String eventName, String icon) {
        Button btn = new Button();
        btn.setHflex("1");
        btn.setIconSclass(icon);
        Attribute attribute = selection.getAttribute(eventName);
        if (attribute != null)
            btn.setStyle("color:red");
        else {
            Element attributeNode = getElementAttribute(selection, eventName);
            if (attributeNode != null)
                btn.setStyle("color:red");
        }
        return btn;
    }

    private Component createEditor(Component component, EventListener<?> el,
                                   String propertyName, Element property) {
        String value = null;
        Attribute attribute = selection.getAttribute(propertyName);
        if (attribute != null)
            value = attribute.getValue();
        // 检查mold
        if ("mold".equals(propertyName)) {
            Combobox combobox = new Combobox();
            combobox.setHflex("1");
            for (String moldName : component.getDefinition().getMoldNames()) {
                Comboitem comboitem = new Comboitem(moldName);
                comboitem.setParent(combobox);
                if (moldName.equals(value))
                    combobox.setSelectedItem(comboitem);
            }
            combobox.addEventListener(Events.ON_CHANGE, el);
            return combobox;
        }
        if ("access".equals(propertyName)) {
            Combobox combobox = new Combobox();
            combobox.setHflex("1");
            for (AccessVo accessVo : accessVos) {
                StringBuffer sb = new StringBuffer(accessVo.getCode());
                if (Strings.isBlank(accessVo.getControlType()) == false) {
                    sb.append("_").append(accessVo.getControlType());
                }
                Comboitem comboitem = new Comboitem(sb.toString());
                comboitem.setParent(combobox);
                if (sb.toString().equals(value))
                    combobox.setSelectedItem(comboitem);
            }
            combobox.addEventListener(Events.ON_CHANGE, el);
            return combobox;
        }
        final String type = property.getAttributeValue("type");
        if (type.equals("booleanType")) {
            Checkbox sb = new Checkbox();
            if (value == null) {
                Object defaultValue = ZulXsdUtil.getDefaultValueForProperty(
                        component, propertyName, true);
                sb.setChecked(defaultValue == null ? false
                        : (Boolean) defaultValue);
            } else
                sb.setChecked(value.equals("true"));
            sb.addEventListener(Events.ON_CHECK, el);
            return sb;
        }
        if (type.equals("intType")) {
            Intbox intbox = new Intbox();
            intbox.addEventListener(Events.ON_CHANGE, el);
            if (value != null)
                intbox.setValue(NumberUtils.toInt(value));
            return intbox;
        }
        if (type.equals("longType")) {
            Longbox longbox = new Longbox();
            longbox.addEventListener(Events.ON_CHANGE, el);
            if (value != null)
                longbox.setValue(NumberUtils.toLong(value));
            return longbox;
        }
        if (type.equals("doubleType")) {
            Doublebox doublebox = new Doublebox();
            doublebox.addEventListener(Events.ON_CHANGE, el);
            if (value != null)
                doublebox.setValue(NumberUtils.toDouble(value));
            return doublebox;
        }
        if (type.equals("decimalType")) {
            Decimalbox decimalbox = new Decimalbox();
            decimalbox.addEventListener(Events.ON_CHANGE, el);
            if (value != null)
                decimalbox.setValue(new BigDecimal(value));
            return decimalbox;
        }
        if (type.endsWith("EntityType")) {
            boolean hasAttributeValue = false;
            if (type.startsWith("logic") && value == null)
                hasAttributeValue = getElementAttribute(selection, propertyName) != null;
            Bandbox box = new Bandbox();
            box.setHflex("1");
            box.addEventListener(Events.ON_CHANGE, el);
            EventListener<?> listener = new EntityEventListener(box, type,
                    propertyName);
            box.addEventListener(Events.ON_OPEN, listener);
            box.setValue(value);
            if (type.equals("dbEntityType"))
                return box;
            Hlayout div = new Hlayout();
            div.setHflex("1");
            box.setParent(div);
            Button btn = new Button();
            btn.setIconSclass("z-icon-edit");
            if (hasAttributeValue)
                btn.setSclass("epeditor-btn epeditor-btn-mark");
            else
                btn.setSclass("epeditor-btn");
            btn.addEventListener(Events.ON_CLICK, listener);
            btn.setParent(div);
            return div;
        } else if (type.equals("styleAttrType") || type.equals("queryAttrType")) {
            Button btn = createEventButton(propertyName,
                    type.equals("styleAttrType") ? "z-icon-css3"
                            : "z-icon-scribd");
            btn.addEventListener(Events.ON_CLICK,
                    new PageEventListener(type.equals("styleAttrType") ? "css"
                            : "sql", type));
            return btn;
        } else if (type.equals("iconAttrType") || type.equals("imageAttrType")) {
            Bandbox box = new Bandbox();
            box.setHflex("1");
            box.addEventListener(Events.ON_CHANGE, el);
            EventListener<?> listener = new EntityEventListener(box, type,
                    propertyName);
            box.addEventListener(Events.ON_OPEN, listener);
            box.setValue(value);
            return box;
        } else if ("src".equals(propertyName) && component instanceof Image) {
            Bandbox box = new Bandbox();
            box.setId("imageBox");
            box.setHflex("1");
            box.addEventListener(Events.ON_CHANGE, el);
            EventListener<?> listener = new EntityEventListener(box, type,
                    propertyName);
            box.addEventListener(Events.ON_OPEN, listener);
            box.setValue(value);
            return box;
        } else if ("src".equals(propertyName) && component instanceof Style) {
            Bandbox box = new Bandbox();
            box.setId("styleBox");
            box.setHflex("1");
            box.addEventListener(Events.ON_CHANGE, el);
            EventListener<?> listener = new EntityEventListener(box, type,
                    propertyName);
            box.addEventListener(Events.ON_OPEN, listener);
            box.setValue(value);
            return box;
        } else if ("src".equals(propertyName) && component instanceof Script) {
            Bandbox box = new Bandbox();
            box.setId("scriptBox");
            box.setHflex("1");
            box.addEventListener(Events.ON_CHANGE, el);
            EventListener<?> listener = new EntityEventListener(box, type,
                    propertyName);
            box.addEventListener(Events.ON_OPEN, listener);
            box.setValue(value);
            return box;
        } else if ("src".equals(propertyName) && component instanceof Audio) {
            Bandbox box = new Bandbox();
            box.setId("audioBox");
            box.setHflex("1");
            box.addEventListener(Events.ON_CHANGE, el);
            EventListener<?> listener = new EntityEventListener(box, type,
                    propertyName);
            box.addEventListener(Events.ON_OPEN, listener);
            box.setValue(value);
            return box;
        }
        List<String> restrictions = ZulXsdUtil
                .getConstraintForAttributeType(type);
        if (!restrictions.isEmpty()) {
            Combobox combobox = new Combobox();
            combobox.setHflex("1");
            for (String enumeration : restrictions) {
                Comboitem comboitem = new Comboitem(enumeration);
                comboitem.setParent(combobox);
                if (enumeration.equals(value))
                    combobox.setSelectedItem(comboitem);
            }
            if (combobox.getSelectedItem() == null)
                combobox.setValue((String) value);
            combobox.addEventListener(Events.ON_CHANGE, el);
            return combobox;
        }
        Textbox textbox = new Textbox();
        textbox.setValue(value);
        textbox.setHflex("1");
        textbox.addEventListener(Events.ON_CHANGE, el);
        return textbox;
    }

    private void openEditor(Component ref, String id) {
        if (!id.startsWith("$") && workbench.checkEditor(id) == null) {
            GetEntityVo ge = StudioApp
                    .execute(ref, new GetAndLockEntityCmd(id));
            if (ge != null) {
                char code = 'U';
                if (!Strings.isBlank(ge.getLockUser())) {
                    code = 'R';
                    Messagebox.show(
                            Labels.getLabel("enitiy.locked",
                                    new String[]{ge.getLockUser()}),
                            Labels.getLabel("message.title.error"),
                            Messagebox.OK, Messagebox.INFORMATION);
                }
                workbench.createEditor(ge.getEntity(), code);
            }
        }
    }

    private class PageEventListener implements EventListener<Event> {

        private String mode;

        private String type;

        private PageEventListener(String mode, String type) {
            this.mode = mode;
            this.type = type;
        }

        @Override
        public void onEvent(Event event) throws Exception {
            String eventName = ((Label) event.getTarget().getPreviousSibling())
                    .getValue();
            AbstractView.createCodeView(
                    new PageEventCallback((Button) event.getTarget(),
                            eventName, type), mode, event.getTarget()).doOverlapped();
        }

    }

    private class PageEventCallback implements EditorCallback<String>, Tablable {

        private Button btn;

        private String property;

        private boolean hasAttribute;

        private PageEventCallback(Button btn, String property, String type) {
            this.btn = btn;
            this.property = property;
            hasAttribute = type == null || type.startsWith("logic")
                    || type.startsWith("pageEvent");
        }

        @Override
        public String getTable() {
            BaseEntity entity = ((EntityEditor) page).getEntity();
            if (entity instanceof PageBean)
                return ((PageBean) entity).getTable();
            else if (entity instanceof ListBean)
                return ((ListBean) entity).getTable();
            return null;
        }

        @Override
        public String getValue() {
            String value = null;
            Attribute attribute = selection.getAttribute(property);
            if (attribute != null)
                value = attribute.getValue();
            else if (hasAttribute) {
                Element attributeNode = getElementAttribute(selection, property);
                if (attributeNode != null)
                    value = attributeNode.getValue();
            }
            if (value != null)
                value = StringUtils.replaceEach(value, new String[]{"&amp;",
                        "&lt;", "&gt;", "&quot;", "&apos;"}, new String[]{
                        "&", "<", ">", "\"", "'"});
            return value;
        }

        @Override
        public void setValue(String value) {
            Attribute attribute = selection.getAttribute(property);
            if (Strings.isBlank(value)) {
                if (attribute != null)
                    selection.removeAttribute(attribute);
                if (hasAttribute) {
                    Element attributeNode = getElementAttribute(selection,
                            property);
                    if (attributeNode != null)
                        selection.removeChild(attributeNode);
                }
                btn.setStyle(null);
            } else {
                if (hasAttribute) {
                    if (attribute != null)
                        selection.removeAttribute(attribute);
                    int pos = -1;
                    Element attributeNode = getElementAttribute(selection,
                            property);
                    if (attributeNode != null) {
                        pos = selection.indexOf(attributeNode);
                        selection.removeChild(attributeNode);
                    }
                    attributeNode = new Element("attribute");
                    attributeNode.addAttribute(new Attribute("name", property));
                    attributeNode.appendChild(value);
                    if (pos >= 0)
                        selection.insertChild(attributeNode, pos);
                    else
                        selection.appendChild(attributeNode);
                } else {
                    if (attribute == null) {
                        Attribute uuid = selection.getAttribute("uuid");
                        selection.removeAttribute(uuid);
                        selection.addAttribute(new Attribute(property, value
                                .toString()));
                        // 保证uuid属性在最后
                        selection.addAttribute(uuid);
                    } else
                        attribute.setValue(value.toString());
                }
                btn.setStyle("color:red");
            }
            zulMapXml = CodeFormatter.formatXML(document);
            Document copy = (Document) document.copy();
            ZulXsdUtil.cleanUUIDs(copy.getRootElement());
            page.updateContent(CodeFormatter.formatXML(copy));
        }

        @Override
        public String getTitle() {
            return property;
        }

        @Override
        public Page getPage() {
            return btn.getPage();
        }

        @Override
        public Editor getEditor() {
            return (Editor) page;
        }

    }

    private class EntityEventListener implements EventListener<Event> {

        private Bandbox bb;

        private String type;

        private String propertyName;

        EntityEventListener(Bandbox bb, String type, String propertyName) {
            this.bb = bb;
            this.type = type;
            this.propertyName = propertyName;
        }

        @Override
        public void onEvent(Event event) throws Exception {
            if (event.getName().equals(Events.ON_OPEN)) {
                if (type.equals("dbEntityType"))
                    AbstractView.createDatasourceView(
                            new BandboxCallback((Editor) page, bb), event.getTarget())
                            .doOverlapped();
                else if (type.equals("imageAttrType")) {
                    AbstractView.createIconView(
                            new BandboxCallback((Editor) page, bb), "image", event.getTarget())
                            .doOverlapped();
                } else if (type.equals("iconAttrType")) {
                    AbstractView.createIconView(
                            new BandboxCallback((Editor) page, bb), "icon", event.getTarget())
                            .doOverlapped();
                } else if (propertyName.equals("src") && !Strings.isBlank(bb.getId()) && bb.getId().equals("imageBox")) {
                    AbstractView.createIconView(
                            new BandboxCallback((Editor) page, bb), "image", event.getTarget())
                            .doOverlapped();
                } else if (propertyName.equals("src") && !Strings.isBlank(bb.getId()) && bb.getId().equals("styleBox")) {
                    AbstractView.createFileListView(
                            new BandboxCallback(bb, "css"), "css", event.getTarget())
                            .doOverlapped();
                } else if (propertyName.equals("src") && !Strings.isBlank(bb.getId()) && bb.getId().equals("scriptBox")) {
                    AbstractView.createFileListView(
                            new BandboxCallback(bb, "js"), "js", event.getTarget())
                            .doOverlapped();
                } else if (propertyName.equals("src") && !Strings.isBlank(bb.getId()) && bb.getId().equals("audioBox")) {
                    AbstractView.createFileListView(
                            new BandboxCallback(bb, "audio"), "audio", event.getTarget())
                            .doOverlapped();
                } else
                    AbstractView.createEntityView(
                            new BandboxCallback((Editor) page, bb),
                            StringUtils.capitalize(StringUtils.substringBefore(
                                    type, "EntityType")), bb).doOverlapped();
            } else {
                String val = bb.getValue();
                if (!Strings.isBlank(val)) {
                    if (type.startsWith("logic")) {
                        if (StringUtils.isAlphanumeric(val)) {
                            openEditor(event.getTarget(), val);
                        } else
                            AbstractView.createLogicView(
                                    new PageEventCallback((Button) bb
                                            .getNextSibling(), propertyName,
                                            type), bb
                                            .getNextSibling()).doOverlapped();
                    } else {
                        openEditor(event.getTarget(), val);
                    }
                } else if (type.startsWith("logic")) {
                    AbstractView.createLogicView(
                            new PageEventCallback((Button) bb.getNextSibling(),
                                    propertyName, type), bb.getNextSibling()).doOverlapped();
                }
            }
        }
    }

    private class PropertityChangeListener implements EventListener<Event> {

        private Component selectedComponent;

        PropertityChangeListener(Component selectedComponent) {
            this.selectedComponent = selectedComponent;
        }

        @Override
        public void onEvent(Event event) throws Exception {
            String propertyName = null;
            if (event.getTarget().getPreviousSibling() == null) {
                propertyName = ((Label) event.getTarget().getParent()
                        .getPreviousSibling()).getValue();
            } else
                propertyName = ((Label) event.getTarget().getPreviousSibling())
                        .getValue();
            Object value = null;
            if (event.getTarget() instanceof Checkbox)
                value = ((Checkbox) event.getTarget()).isChecked();
            else
                value = ((InputElement) event.getTarget()).getRawValue();
            if (value == null || value.toString().trim().equals(""))
                value = "";
            String type = ZulXsdUtil.getTypeOfAttribute(
                    selection.getLocalName(), propertyName);
            boolean isDefaultValue = ZulXsdUtil.isDefaultValueForProperty(
                    selectedComponent, propertyName, value,
                    "booleanType".equals(type));
            Attribute attribute = selection.getAttribute(propertyName);
            if (attribute == null && value.toString().length() > 0) {
                Attribute uuid = selection.getAttribute("uuid");
                selection.removeAttribute(uuid);
                selection.addAttribute(new Attribute(propertyName, value
                        .toString()));
                // 保证uuid属性在最后
                selection.addAttribute(uuid);
            } else if (attribute != null
                    && (value.toString().length() == 0 || isDefaultValue)) {
                selection.removeAttribute(attribute);
            } else if (attribute != null && value.toString().length() > 0) {
                if (value.equals(attribute.getValue()))
                    return;
                if (!isDefaultValue) {
                    attribute.setValue(value.toString());
                } else {
                    selection.removeAttribute(attribute);
                }
            }
            if (isEvent(propertyName) || type.equals("logicEntityType")) {
                if (!Strings.isBlank(value.toString())) {
                    Element attributeNode = getElementAttribute(selection,
                            propertyName);
                    if (attributeNode != null)
                        selection.removeChild(attributeNode);
                    ((Button) event.getTarget().getNextSibling())
                            .setSclass("epeditor-btn");
                }
            } else {
                try {
                    Mirror.me(selectedComponent).findSetters(propertyName)[0]
                            .invoke(selectedComponent, value);
                } catch (Exception ex) {
                }
            }
            zulMapXml = CodeFormatter.formatXML(document);
            Document copy = (Document) document.copy();
            ZulXsdUtil.cleanUUIDs(copy.getRootElement());
            page.updateContent(CodeFormatter.formatXML(copy));
        }
    }
}
